RL agent ladders for cold-start skill prediction: sprint, minesweeper, and what transferred - #1
Merged
Merged
Conversation
Generalises the Connect Four pipeline to all four games so the comparison the project turns on - what property of a game decides how early skill is legible - can be run with the method held fixed. New: games.py extended with restart/ply-capping/reward normalisation, nets.py (conv for board games, MLP for vector games), selfplay.py, ladder.py, profile.py, predict.py, tactics.py, and a features/ package with a distinct feature set per game. Connect Four is deliberately built twice; the pgx port is differential- tested against the PettingZoo-validated original feature by feature. Three bugs the measurement caught rather than the code: - One-step targets do not work on long episodes. The first backgammon ladder spanned 32 Elo with the untrained network ranked third - everything trained, nothing learned, no error anywhere. Targets are now n-step, set per game against episode length; backgammon went from 0.57 to 0.85 against random and its ladder from 32 to 184 Elo. - Counting hands won would have ranked a fold-everything Leduc policy first. Pairwise results now fit the mean normalised result, identical to wins + draws/2 for the games that are simply won or lost. - Leduc's pot tops out at 13, not the 11 a random rollout samples, so the reward scale was wrong for exactly the biggest pots. Derived now, not sampled. Tactical density is measured by two-ply search rather than asserted, which shows the asserted "medium" for backgammon was wrong (0.5%). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three features were flagged as not earning their place. Checking each against the data rather than against the prior that put it there: - bears_off fired on 0.000 of profiled moves in every tier and is removed. A backgammon game runs a couple of hundred plies; the telemetry window is the first twenty moves, so nobody in it is bearing off. - is_pass is likewise constant in Othello's window and is removed. Passes are still handled - they just are not reported. - pip_gain was removed on the argument that the die value is the dice's choice rather than the player's, and the argument was wrong: accuracy fell 6.5 points at one move and 5 at two. Which die you are still able to play is decided by the position you left yourself. Restored. Adds moves_rearmost (running the back checker against building at home), which is the choice pip_gain was mistaken for. Keeping both beats the original set at every move budget: n=1 0.492, n=2 0.606 (was 0.602), n=20 0.362 (was 0.332). Also corrects two docstrings that asserted received wisdom the measurement contradicts: Othello's discs-flipped tell runs the wrong way here (1.545 -> 1.638 up the ladder), and backgammon's top tier over-stacks more than its bottom, not less. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Orders the open questions cheapest-invalidating-first. Two items (the cross-game speed/efficiency heuristic, and handicapping versus undertraining in the games already built) need no new environment and could each retire a large part of the remaining work. Records the constraint that emerged while planning: nothing requiring the target game's human data may be used to build the method, since at a real launch it does not exist. Human data tests, once, at the end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The lightweight cross-game heuristic and the agent-ladder work answer different questions, and neither is contingent on the other's result. The heuristic is the baseline the agent track is scored against - an agent number means little alone and a lot against a known simple baseline - so it goes first for calibration, not as a gate. Adds E7, the head-to-head that makes the comparison explicit, and a track column so the commitment is legible at a glance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Renumbers the head-to-head and deployment experiments to E6 and E7, and removes the Tenhou row from the data source table. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Builds the lightweight track. coldopen/human/ ingests rank-labelled public records; coldopen/crossgame.py runs the transfer matrix that E1 is actually about - fit on game A, predict skill in game B, with the within-game diagonal only there as the ceiling to measure the off-diagonal against. The client is deliberately slow: one request a second, an identifying User-Agent with a contact route, disk cache so iterating on features costs nobody anything, and Retry-After honoured. Player ids are hashed at the boundary; nothing downstream can leak an identity it never received. TETR.IO sampling had to be redesigned. Walking the leaderboard from the top returned sixty accounts that were all X+, which is exactly the population the cold-start problem is not about. It now enters the leaderboard at eighteen rating points chosen to land in each rank. Pilot result on 54 players across all 18 ranks: Spearman 0.892 against real rating from a single round, 0.956 from three, using two features and a ridge regression grouped by player. That is the baseline the agent track must beat. Confirms the cross-person speed/efficiency correlation the E5 ladder design assumes (rho = 0.878). Refutes one prior: VS/APM is flat across ranks, so VS carries no skill signal beyond APM. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…result The two-axis transfer matrix looks like a strong positive - a model fitted on StarCraft II ranks TETR.IO players at 0.911, against a 0.974 within-game ceiling. The ablation says that number is not evidence of a transferable shape. Each axis alone transfers as well as or better than the pair (speed only 0.926, efficiency only 0.927, both 0.911 for skillcraft->tetrio), and the same holds in the other direction. Combining the axes helps within a game - 0.974 against 0.924 for speed alone in TETR.IO - and hurts across them. A standardised single-axis model has nearly nothing to transfer, since ranking by a z-score needs no fitting, so its cross-game score is that axis's within-game correlation in disguise; speed-only scores an identical 0.661 both within SkillCraft and transferred from Tetris, which is the giveaway. So the strong form of E1 is not supported. What is domain-general is that each axis correlates with skill; the weighting between them is game-specific. The ablation now runs as part of the standard output rather than as a one-off, because a pair that does not beat its own best single axis has not shown anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lichess supplies the cleanest instance of the schema yet and needs no key, no scraping and no rate limit: speed from clock deltas, efficiency from Stockfish annotations, and an Elo computed from results and nothing else. Monthly dumps are tens of gigabytes but the games are independent, so the reader streams and stops - 12,304 player-rows cost a few hundred megabytes. With three games the ablation becomes decisive. A no-fit control that ranks players by the z-scored axis, fitting nothing, scores identically to the fitted single-axis transfer - and those fitted scores are the same whatever game they were fitted on. Every column is constant across sources, so single-axis "transfer" carries nothing; it is the target's own correlation in disguise. That leaves the two-axis pair as the only place a learned relationship could live, and it loses to the best single axis in five of six ordered pairs. The reason is visible in the per-game breakdown: chess at a fixed time control is 0.10 speed against 0.36 efficiency, StarCraft is 0.66 against 0.44, TETR.IO is 0.93 against 0.93. The weighting is a property of the game, not of skill. The weak claim survives and is still useful: standardise whichever axis a game exposes and a skill estimate comes free, with no model and no data from the target game. Worth 0.93 in Tetris, 0.66 in StarCraft, 0.36 in chess. Also fixes a test that was wrong rather than a bug: dropping the evaluation on one move charges both players, because giving an advantage straight back is itself a mistake. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nethack.alt.org publishes every game it has ever recorded as xlogfiles - 8.6 million games in under 100 MB - and it is the only corpus found with many games by the same identified player. That makes it the only place the cold-start question can be asked directly instead of by proxy: predict how good somebody turns out to be from the first games they ever played. Labels are held out in time, not just in rows. Skill is scored from the second half of a career and predicted from the first, so the observation window is never part of what it predicts. Scoring a career from all of its games would not look like a bug, it would look like an excellent result. 967 careers, 46,660 games: one game gives rho 0.303 against later career, three 0.369, ten 0.497, twenty 0.547. With four games the E1 asymmetry is clear and sharper than the earlier reading. Efficiency - output per action - is positive and useful everywhere: 0.93 Tetris, 0.65 NetHack, 0.44 StarCraft, 0.36 chess. Speed is game-specific: 0.93 in Tetris and 0.66 in StarCraft but 0.17 in untimed NetHack and 0.10 in chess at a fixed clock. Time pressure is the obvious mechanism. Importing another game's weighting is destructive rather than merely unhelpful: the speed-dominant SkillCraft fit applied to NetHack scores 0.158, four times worse than ignoring SkillCraft and using NetHack's own efficiency axis. Honours the two parsing traps the archive documents: the 3.4.3-era file uses colon separators, and the field set widens over the years, so fields are read by name and all are optional. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dataset would have been the strongest entry - five games under one collection protocol, which is the confound every pair in the current table still carries - but the domain has been re-registered as an unrelated site and the data path is gone. Leads recorded rather than chased, along with the reason they may not be worth it: the trajectories carry no player identifier, so they could not answer the per-career question NetHack now does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds three ways to degrade the strongest checkpoint instead of catching an undertrained one: epsilon (throw the move away, the control that computer chess already knows feels unhuman), temperature (sample from the action values, so mistakes stay ordered), and blindspot (hide a band of the board, modelling attention rather than decision noise). They expose act() and are delegated to from epsilon_actions, so the league, profiler and classifier need no changes. At a matched Elo range - 689 against the trained ladder's 736 - the temperature-handicapped ladder gives 0.287 from one move where the undertrained one gives 0.468, and rises to 0.384 by sixteen where the other reaches 0.619. Skill is much less legible early when weakness is decision noise rather than an undertrained value function, which was the written prediction. That makes the project's headline result partly a property of the generator rather than of the game, and it sharpens the standing prediction that transfer to humans will compress: human error is ordered, so if people resemble the temperature ladder then one move is worth nearer 0.29 than 0.47. Two side findings. The handicapped ladder ranks better than the trained one (concordance 0.978 against 0.912), since the dial controls strength directly while training only correlates with it. And blindspot has a floor at about 0.80 against random however blind it is, because a consistent policy beats random whatever it is consistent about - this project's opening trap, reappearing. Blindspot was reparameterised after the first version failed to degrade play at all: what matters is how much of the board goes unattended, not how often. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Full runs for temperature, epsilon and blindspot at 300 games per league pair and 600 sessions per tier. All four ladders span 612-736 Elo, so the comparison is at matched measured strength. Undertraining is by far the most legible from one move: 0.468 against 0.281, 0.249 and 0.307. The project's headline finding is therefore substantially a property of how the ladder was built rather than of the game. The prediction that handicapping would produce MORE structured errors is wrong, and backwards. Measuring blunder-profile concentration puts undertraining highest (0.33, 0.34, 0.25, 0.37 across the weak tiers) and the noise handicaps lowest (0.12-0.21). An undertrained network has systematic gaps - it has learned something about the centre and nothing about blocking - so its mistakes have a shape. Noise spread evenly across positions spreads mistakes evenly across error types. That reverses the mechanism and strengthens the conclusion: undertrained agents are easy to classify early BECAUSE their errors are systematic, and one move can expose the profile of what they have not learned. Noise-degraded agents need observations to estimate a rate. Blindspot has its own signature - flat to three moves, a jump to 0.536 at five, then a plateau - which is what an occasional catastrophic miss looks like. The useful output is that the three generators have distinguishable fingerprints in curve shape and error concentration. Computing the same two signatures on a human corpus turns "which failure model are humans?" into a measurement, and the human side of that needs no agents at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two changes, one of which withdraws an earlier result. The tier-spacing control: the first E2 comparison matched only the total Elo range, which let the trained ladder's tiers sit 1.4x further apart than the handicapped ones - and tier gap is the main driver of how hard classification is, a point this project makes about Leduc and then failed to apply to itself. Generators now run through one code path with tiers pinned to fixed ratings. The finding survives the control and is stronger for it: at a mean tier gap of 121 against temperature's 139, so with the confound now working against it, the trained ladder still gives 0.522 from one move where temperature gives 0.292 and epsilon 0.249. The distillation ladder: train a student to imitate the strongest checkpoint and snapshot it as it learns. This is a third kind of generator, and possibly the closest of the three to how people are bad - a partially-trained imitator has picked up the common patterns and not the rare ones, which is what "strong at what you have drilled, weak elsewhere" means. It also solves a range problem the others cannot. Handicapping only reaches down from a finished agent and undertraining only reaches up from random; a student spans both ends in one run. On Connect Four it goes from 0.127 teacher agreement at initialisation to 0.954 after four thousand steps, smoothly. That range property is what makes it the answer for NetHack, where the overlap check found random agents sitting at the 5th percentile of human games while AutoAscend's median sits around the 85th. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
All five now run through one code path with tiers pinned to the same six ratings, so tier spacing cannot explain the difference. The trained ladder ends up with one of the narrowest gaps (121) and still nearly doubles every other generator at one move: 0.522 against 0.283 distilled, 0.308 blindspot, 0.292 temperature, 0.249 epsilon. Distillation is what makes this decisive. It is also a form of undertraining - a student snapshotted partway to competence - and it behaves like the handicaps, not like the RL ladder. So the legibility is not a property of being partly trained. It is specific to RL checkpoints, most likely because an early DQN has a systematically distorted value function (something about the centre, nothing about blocking) whose blunder profile is a fingerprint, where a cloning student is wrong in scattered places instead. Error concentration separates them the same way: the trained ladder stays concentrated at every strength, while distillation and blindspot are concentrated only at the weak end and become uniform as they improve - which is the more plausible description of a person. Three curve families fall out - trained (high from move one), distilled and blindspot (jump at five, plateau), temperature and epsilon (slow climb) - and they are fingerprints a human corpus can be matched against without needing any agents. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The claim that most of the signal arrives on move one was measured on a ladder built by snapshotting self-play, and rebuilding it four other ways at matched tier spacing puts the same number at 0.25-0.31 instead of 0.52. The outward-facing document should not keep stating the strong version. Also states plainly that no game yet has both an agent ladder and human data, so the transfer question remains untouched rather than merely pending. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nformatively Othello turned out to be the one game where both halves already existed. The French Othello Federation publishes 125,000 tournament games with full move sequences, free, and Othello already had an agent ladder, a reference network and a feature extractor - so both populations could go through identical code. Two traps handled: WTHOR omits passes, so replaying the list literally desynchronises the board and misattributes every later move; and it ships no ratings, so skill is fitted from the results with the same Bradley-Terry code the agent league uses, which keeps both populations on one scale. 60 of 60 games replay legally and the fitted human spread is sensible. The comparison then fails three ways at once, and each says what to fix. The corner features are nonzero in 0.013% of human move rows: the window is a player's first twenty moves and competent Othello contests corners in the endgame. The set that separates agents measures events that do not happen when humans are watched. Agents only produce them by playing badly enough to give corners away in the opening. ref_agreement is inverted - stronger humans agree with the reference less - because the reference is a mediocre DQN and tournament players deviate from it by being better. A yardstick has to outrank everyone it measures. And the ladder tops out at 641 Elo over random while every WTHOR player is a tournament entrant, so the populations never meet. This is the manifold-overlap risk the plan names as the central threat, measured on the first real attempt, and invisible from the agent-only experiments - all four of which looked healthy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The versus engine was the expensive part - kick tables, T-spin corner rules, garbage cancelling order, the multiplier combo, the garbage cap - and it turns out not to be needed. A 40 LINES record already carries inputs and piecesplaced, so inputs/piece is finesse: keystrokes spent against the minimum the placement needed. Rules derived, no opponent, no attack table, no engine. Same class of feature as missed_win. Checked across the ladder rather than assumed: 47 of 54 sampled players have a sprint record with coverage at every rank, finesse runs 6.71 at D to 2.60 at X+ and quad rate 0.14 to 0.76, both monotone. The design this permits avoids all three failures Othello hit. The label is league TR, earned in versus, while the features come from sprint - different modes, so the label cannot restate the features, which is what disqualified Jstris. Finesse and quad rate need only the rules, so no yardstick can invert. And they fire from the first piece, so no window mismatch. What is given up is VS/APM, the downstacking signal, which E1 already measured as flat across every rank. The sacrificed part is the part already shown inert. One consequence recorded: the simulator has to be input-level rather than placement-level, since finesse only exists if the agent emits keystrokes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The simulacrum workflow end to end: spec.md as the single source of truth,
a readable reference implementation, an independently written batched torch
implementation, and the full validation battery - PASS, eligible for training.
Bit-exact differential over 8 seeds x 300 steps, batch independence, twelve
invariants swept, auto-reset, determinism, replay, compiled parity. A third
hand-transcription of the SRS tables guards both implementations against a
shared transcription slip.
One step is one keystroke, and time is deterministic accounting: each action
costs declared integer milliseconds plus a per-agent LATENCY parameter, with
gravity and guideline lock delay running on the virtual clock. Finesse exists
because steps are keystrokes; sprint time and PPS exist because the clock is
state; the reward (clear bonus minus elapsed ms) is the human sprint objective
rather than an analogue; and the speed/accuracy coupling that E5 called for
emerges from the physics instead of being wired in.
The invariant sweep caught what the differential test could not: both
implementations faithfully implemented the same broken spec rule - a piece
lifted off a ledge at the lock-delay reset cap kept stale lock delay. Fixed in
the spec first ("leaving the ground discards lock delay"), then in both.
Throughput: the harness headline of 1x vs reference is an artifact (its
reference loop skips observation building). With observations, 13.8k steps/s
reference vs 74k compiled at n=1024 - a 5x training-shape speedup, an order of
magnitude above the wall rate the Connect Four ladder actually trained at.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
coldopen/tetris.py extracts per-episode telemetry from batched rollouts by watching the env from the outside: a queue shift means a piece was consumed, and a consumed piece is a lock unless the hold swapped it in, with terminal corrections for the finishing or topping-out lock that never spawns. The feature set mirrors what TETR.IO's API reports for a human 40L record, so the comparison is column for column. Seven tests drive scripted action streams with known counts. coldopen/train_sprint.py is single-player DQN with the lessons already paid for: n-step returns (the clear bonus is many steps downstream of the stacking that earns it) and log-spaced checkpoints. New here: potential-based shaping on holes and stack height, training-only, optimal-policy-preserving - random play essentially never clears a line, which is the sparse-reward wall the Tetris literature warns about. Every reported number is unshaped. coldopen/human/tetrio_sprint.py pulls each sampled ladder player's best 40L record through the cached polite client: same sample as the league ingestion, league TR as the label, sprint statistics as the features. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Extending a training run should cost a flag, not a retrain: the newest checkpoint in --out seeds the network, the schedule skips already-saved tags, and the log appends. The replay buffer and epsilon restart cold - a brief re-exploration bump, cheaper than serializing a 600MB buffer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Finesse spans 4.86 to 2.81 inputs per piece, quad rate 0.03 to 0.69, and - unpredicted - hold usage rises about 4x with skill. Holds per piece is a pure planning signal with no speed component, making it a fifth human-comparable feature. This table is the manifold the agent ladders must land on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eacher The 3M-step run ended at 0.00 mean lines: a brief blip of clears at 227k never returned, and the 1.2M-3M curve is flat. Keystroke-level control needs about five coordinated decisions per placement and ten good placements per line, which epsilon-greedy exploration essentially never strings together - the documented Tetris RL wall, now measured here. The checkpoints are kept: they are the undertrained-RL generator's floor rungs. The teacher that breaks the wall is scripted, in the AutoAscend pattern: enumerate every (rotation, column) placement, score the resulting board with Dellacherie-style hand weights (holes, height, bumpiness, clears), emit the keystroke sequence - DAS to walls, taps elsewhere - and replan per piece. No human data anywhere, so ladders built from it stay cold-start legitimate. First evaluation: finishes 15/16 sprints, 41.5s mean, finesse 3.02 inputs/piece. Against the human table that is an odd and informative shape - finesse near rank x (3.07), quad rate near rank d (0.01 vs 0.03), holds below the entire human range (0.0 vs 0.07 at the floor). The bot is off-manifold as a package even though each axis alone looks human; recorded as the first overlap observation rather than smoothed over. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured first: dialling the env's LATENCY from 17ms to 1400ms left the scripted teacher at 40.1 lines, 100% finish and 3.05 inputs/piece at every setting. Only pps and elapsed time moved. Gravity displaces pieces vertically and a hard drop lands them at the bottom regardless, so deliberating slowly never costs a placement. The dial produces slow-but-flawless players, and E1 established humans are not that - the slowest ranks have the WORST finesse (4.86 against 2.81 at the top). So the coupling is now explicit. SprintBot.skill moves two knobs together: Gaussian noise on candidate placement scores (ordered mistakes, the property E2 found separates temperature-style degradation from uniform noise), and fumbled keystrokes - taps walked back, which cost finesse without changing where the piece lands. The resulting ladder covers the human range on three axes: finesse 3.05-4.72 against a human 2.81-4.86, pps 7.07-0.23 against 6.01-0.84, sprint time 15s-464s against 17s-135s. Quad rate does not: every rung sits at ~0.01 while humans span 0.03 to 0.69. Recorded as the open manifold gap. Also fixes a latent SprintBot bug: a plan surviving auto-reset was applied to the next episode's board. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured the compounding-error failure rather than assuming it: the cloned student reached 0.995 per-action agreement with the teacher and still cleared 4.3 lines against the teacher's 40, never finishing a sprint. Over ~300 keystrokes even that accuracy gives 0.995^300 = 22% odds of a clean run, and one wrong keystroke lands the student on a board its teacher never built, where its next action is worse than a guess. DAgger (Ross et al. 2011) closes the distribution mismatch: roll the student out, ask the teacher what it would play in the states the student actually reached, aggregate, keep training. Rounds interleave with the snapshot schedule so ladder rungs spread across the whole curriculum. --rounds 1 reproduces plain cloning for comparison. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…idn't The agent-versus-human overlap check reported every generator "100% inside the human range" on quad rate, holds and B2B. All three were false. Pooled human quad rate spans 0.00-1.00, so a bot pinned at 0.00 - below every human alive - passes a range test. Replaced it with coverage (which human rank does this rung look like?) and coherence (do the rung's features agree?). The v1 bot scored 8.0 ranks of median disagreement: rank-16 finesse, rank-0 stacking. The cause was the objective. W_LINES paid for any immediate clear, so the bot cashed singles and never stacked four rows. v2 keeps a well, pays for quads, charges for partial clears while safe, and holds when the swap is better. Two bugs found by reading rather than by a failing test: - the placement search ran x in range(-2, 9), so column 9 was unreachable for any piece whose cells all sit at dx = 0 - including the vertical I that a quad requires; - no hold logic at all, leaving holds/piece below the weakest human rank. Swept the well-defence weights: at W_WELL_FILL = -14 the bot defended the well to the death (10 lines, 0% finish); at -6 it finishes every sprint with the same quad rate (41.4 lines, 100%, quad 0.61 against rank x+'s 0.69). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DQN from scratch never cleared a line in 3M steps, which leaves the "trained RL" ladder at zero on every rung and stage 5 with nothing to compare distillation against. The cause is structural, not a tuning miss: a quad needs nine columns stacked flat and a vertical I dropped into the tenth, about forty correlated keystrokes. Random exploration never assembles that, so every trajectory scores alike and there is no gradient. Potential-based shaping on holes and height did not bridge it. --init warm starts the Q-net from a distilled student, the standard clone-then-improve answer to hard exploration. The student's head was trained as classification logits and is read as Q-values: a scale mismatch, not a semantic one, since the argmax is what the policy uses and Bellman updates rescale the head quickly. Needs a low --eps-start or the decay window is spent destroying the policy it was handed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… humans Two measurements forced the redesign. Latency alone is a pure speed dial (identical finesse at 17ms and 1400ms), and degrading judgement with noise instead killed the bot: at noise 8 every rung below skill 0.7 topped out. A ladder whose lower rungs cannot finish measures survival, not skill. So skill now interpolates the OBJECTIVE between expert well-and-quad play and beginner take-any-clear. Both survive; they differ in what they are trying to achieve. The un-fitted ladder spans 15.4s to 272.3s with in/pc 3.27 to 4.32, quad 0.665 to 0.059 and pps 7.46 to 0.40, every rung finishing. Reverted two constants I had fitted to the human table, which would have made the overlap test circular: - hold margins 0.3/4.0, set because human hold usage never falls below 0.070; - strategy = skill ** 0.7, set because a linear blend put quad rate at 0.265 where the rank-matched humans sit at 0.487. Both fitted values are recorded in EXPERIMENTS.md rather than deleted: the gap between fitted and un-fitted is how much of the overlap would have been manufactured. At the top rung it turns out to be almost nothing (15.5s/0.666 fitted against 15.4s/0.665 un-fitted), which is the useful part of the result. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…estion Distillation kept failing in a way plain agreement numbers hid: a student at 98.6% agreement on the teacher's own states scored 25.8% on the states it actually reached, cleared 1.8 lines, and spent 60 inputs per piece without locking. Adding DAgger rounds made it worse, not better. The cause is that the plan-following teacher is not a Markov policy. Which keystroke it plays depends on how far through its stored plan it is, and plan progress is not in the observation. So "what would the teacher do in this state?" - exactly the question DAgger relabelling asks - has no well-defined answer. Hold made the damage visible: the teacher re-decided to hold on every step the student declined to, so 39.6% of relabelled targets came back `hold` against the student's 1.1%, one persistent disagreement flooding the set. markov=True derives the action from observable state alone: pick the target placement, emit the one keystroke that moves toward it. It plays identically to the plan-following version (39.2 lines, in/pc 3.27, quad 0.674, hold 0.222 either way), so this costs nothing but a re-search per step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Agreement measured on the teacher's own states was 98.6% while the policy was unusable: 1.8 lines, 60 inputs per piece, and 25.8% agreement on the states the student actually reached. Watch the second number, not the first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Coverage, the gate Othello failed, now passes: every feature traverses most of the human ladder with 0% off-manifold (pps 14%), against a v1 whose quad rate was pinned at rank d and whose hold usage sat below every human alive. Coherence is still 8.0 ranks of median disagreement, but a leave-one-out pass names the two causes, and neither is a strategy error: - holds per piece runs 4.1 ranks weak. The hold policy is a one-ply greedy swap check; humans use the slot to plan, and keep using it down to rank d. - inputs per piece runs 2.9 ranks strong. The emitter computes the exact keystroke sequence, so even weak rungs have finesse real weak players lack. Quad rate, the feature v1 got most wrong, is now the best calibrated of the five at +0.4 ranks, with nothing fitted to achieve it. Also memoise the Markov target per (board, piece): it is unchanged while a piece falls, so this restores one search per piece instead of one per keystroke, with identical play. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The encoding is the student's entire action space, so an off-by-one would mislabel every example and present as "the net just didn't learn". And a refactor that quietly wired own-state agreement to pool agreement would remove the only signal that caught three distillation failures, where the two diverged by 4-13x. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
load_checkpoints hardcoded a 10-way head, so placement checkpoints would not load at all. They also need a different rollout: a placement student picks a target and a deterministic emitter walks to it, so unlike the keystroke students it has no limit cycle to escape and stays greedy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The section had grown in the order things happened, which buried the ladder result behind the distillation failure and the validation-gate incident. Now: what the humans look like, what was wrong with the metric and the teacher, where the cold-start line runs, THEN the result, then the generator that failed, then the process failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Averaged over 300 steps of each policy's own play: boards max height holes bumpiness teacher 6.85 0.45 10.01 student 9.78 13.50 13.86 The teacher essentially never makes a hole, so the pool contains almost no example of what to do on a board with thirteen of them - and that is the only kind of board the student ever sees. It is not failing to learn the function; it has never been shown the domain it operates in. 97.2% agreement on the teacher's pool against 26.7% on its own boards is the same fact as a metric. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…llegal ones markov_action emits HARD_DROP only once the piece reaches (target_rot, target_x). The teacher never asks for an unreachable target because its search only returns legal ones - but an unmasked student can, and then the piece never arrives, never drops, and the policy stalls forever. That is what the run was showing: inputs per piece climbed to 50.8 against the teacher's 3.3 while own-state agreement fell from 0.268 to 0.136, which I had been reading as DAgger failing to help. LEGAL is a precomputed [7, 44] column-bounds mask, verified to agree exactly with the full search on an empty board (34-36 legal placements per piece). Applied in placement_policy and in own_state_agreement, so the diagnostic measures the same policy that plays. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…locked Masking targets to legal columns was necessary but not sufficient - inputs per piece came back at 86.4 against the teacher's 3.3. Two causes, both about reachability rather than legality: - The student re-predicted every step, and the observation renders the active piece at its current height, so the prediction flipped as the piece fell and the emitter turned around mid-approach. The target is now committed once per (board, piece) and cached, exactly as the Markov teacher memoises its search. Training already supplies the invariance this leans on - the same board and piece appear at every fall height with the same label - but the student only approximates it, and caching makes it exact. - A legal destination is not reachable from everywhere. Beside a tall stack the wanted rotation or sideways step can be physically blocked, and markov_action only drops once the piece arrives, so it tapped forever. markov_action now takes y and hard-drops when the move it wants does not fit. The teacher is bit-identical either way (40.8 lines, in/pc 3.33, quad 0.685, hold 0.339), because its targets are always reachable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aps at 31 Ran the control I should have run first: replace the student's net with the teacher's own answers and see what the pathway can do at best. Without hold it scores 31.0 lines at a 50% finish rate and quad rate 0.349, against the teacher's 40.8 and 0.685. That is a ceiling, not a training gap - and half the oracle's runs top out, while every one of the 396 human sprint records is a finish. A student without hold could never reach the human manifold however well it learned. With hold as a 45th action the same oracle scores 36.8 lines, 83% finish, quad 0.621, holds 0.337 - close enough to the teacher to be worth training against. Hold steps are now labelled rather than dropped from collection, legality is computed per step from hold_used rather than per piece, and teacher_labels() gives DAgger and the diagnostic one shared definition of "what the teacher would do" so they cannot drift apart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
oracle_ceiling() runs the pathway with the teacher's own answers substituted for the student's. It separates "the student has not learned" from "the pathway cannot do better", and those are indistinguishable from the outside - which is what cost most of the work on this generator. The check takes about a minute and now runs at the top of every distillation, with its result stored alongside the training log so no curve can be read without it. It is what caught hold missing from the action space: a 31-line, 50%-finish ceiling against a human population where every record is a finish. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Corrupting a known fraction of the oracle's placements isolates imitation
accuracy from every other variable - same pathway, emitter and teacher:
accuracy lines finish
1.00 36.8 0.83
0.98 40.2 0.92
0.95 27.8 0.42
0.90 10.2 0.00
0.80 5.6 0.00
0.60 0.8 0.00
0.40 0.3 0.00
Nothing finishes below 95% per-placement accuracy, and every one of the 396
human records is a finish. So the whole human-relevant range is compressed
into accuracy 0.95-1.00, and everything below 0.90 is indistinguishable
rubble. A ladder needs a dial whose intermediate settings give intermediate
play; imitation accuracy is not one here.
Second finding in the same data: the DAgger student at 0.834 agreement cleared
1.3 lines where random corruption at 0.80 clears 5.6. A learned policy is
worse than noise at the same error rate, because its errors are systematic
rather than independent - it is wrong the same way on similar boards, so the
mistakes compound instead of averaging out.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Distillation is disqualified as a ladder generator for sprint, for a reason that generalises: a ladder needs a dial whose intermediate settings produce intermediate play, and imitation accuracy is a step function here - nothing finishes below 95% per-placement accuracy, while every human record finishes. That is also why the scripted dial works: it varies strategy, which degrades smoothly, not accuracy, which does nothing until nearly perfect. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sprint_transfer.py fits a ridge model on agent episodes labelled by their own
virtual sprint time - no human quantity anywhere - and applies it once to the
396 human records. Reported beside the controls E1 showed are indispensable:
rank by one standardised feature with no model at all, a human-fitted
cross-validated ceiling, and a speed-versus-judgement ablation, because a
model that only rediscovers "faster is better" has not earned a simulator.
Also records what "early" can mean here: sprint gives a full telemetry row
from ONE game against a rank that takes many matches to establish, which is
the cold-start question in its natural form. The within-game version ("how
many pieces in?") is not answerable from this data - the API returns
game-level summaries, not replays, so a human record cannot be truncated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The test the project exists for, and it is negative. method rho vs true rank needs a simulator pps alone, no model +0.932 no inputs_per_piece alone +0.773 no human-fitted 5-feature ridge +0.923 no (needs labels) agent-fitted 5-feature ridge +0.660 yes Ranking 396 players by one number beats the agent-fitted model by 0.27. The human-fitted model also fails to beat pps alone, so the extra features add nothing even with labels and cross-validation - this is not agents failing to capture something humans have. The ablation reproduces E1's costume finding exactly: fitting pps ALONE on agents gives rho +0.932, identical to the no-fit ranking. A one-axis fitted model is the no-fit ranking. Judgement features alone give +0.635, so they do carry real signal; adding them to pps is what destroys it (+0.660). Diagnosis, and it is the useful part. Every feature has the right sign in both populations. But the agent features are far more collinear than human ones - holds x pps +0.90 against +0.46, quad x pps +0.85 against +0.47 - because a single coupled skill dial makes every feature move together. Ridge splits weight arbitrarily among redundant predictors (it gave pps a NEGATIVE coefficient) and that arbitrary split does not transfer. A one-dimensional ladder produces one-dimensional telemetry. The coupling that made each rung human-shaped is exactly what breaks multivariate transfer. Testing a decoupled skill x latency grid next. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ble here Decoupling the ladder into a skill x latency grid identifies pps's true effect, so the learned weight goes from -0.40 to +0.73 and transfer jumps 0.660 -> 0.927. It converges on the trivial solution without exceeding it: pps alone, no model, is 0.932. The signal beyond speed is real but useless for ranking. Controlling for pps, holds still correlates +0.435 with rank, finesse +0.393, b2b +0.346. Yet an exhaustive search over all 16 subsets containing pps, fitted on humans with 5-fold CV, found nothing that beats pps alone (0.9334). The residual describes variation within a rank, not the ordering between ranks. Design conclusion, which is the part worth keeping: 40 LINES is a time trial, so its ground truth is nearly a single observable axis and there is no headroom for a simulator to fill. That was knowable in advance from E1's own tetrio column (0.926 from speed alone). The check to run BEFORE building an environment: how well does the best single observable feature already predict rank? At 0.9 there is no room. Same discipline as the overlap gate, one level earlier. E1 measured Lichess at 0.358 from its best single axis and SkillCraft at 0.661 against sprint's 0.932, so the agent track should be pointed there instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
I scored the agent-fitted model against "rank by pps" and concluded the
simulator earned nothing. That comparison is invalid. To rank players by pps
you must first know pps is the discriminating axis and which direction it
runs, and that knowledge comes from labelled human data - precisely what does
not exist at a cold start. The baseline was allowed to peek at the test set's
labels; the method under test was not.
What the grid model actually learned from ZERO human labels: weights
[0, 0, 0, 0, +0.73]. It found on its own that pps is the axis and the other
four are noise. That is not a failure to beat the baseline - it IS the
baseline, derived without the labels the baseline needs.
The honest question is how many labelled games buy the same thing. Same
held-out records for every method, 200 splits per size:
labelled games human-fitted agent grid (0 labels)
5 +0.828 +0.927
20 +0.907 +0.927
80 +0.918 +0.927
300 +0.920 +0.922
A model fitted only on agents is not beaten by a model fitted on humans at any
sample size tested, so the simulator is worth more than 300 labelled games. At
cold-start sizes the gap is large. The coupled ladder's 0.660, which read as a
failure against 0.932, is a usable ranking from the very first game with no
rating, no match history and no labels anywhere in its construction.
Still standing: the collinearity diagnosis (build ladders with as many
independent axes as the skill being modelled), and that no subset beats pps on
rank ordering even fitted on humans, so sprint really is close to a one-axis
game. Withdrawn: that the simulator earned nothing, and that a
single-axis screen should gate whether an environment gets built.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Checked minesweeper.online before crawling it: User-agent: * Allow: / Disallow: /chat Disallow: /chat-history Disallow: */password-reset/* Disallow: */invoice/* Permissive for what this project wants, with no Crawl-delay declared. But urllib.robotparser matches rules in FILE ORDER and returns the first hit, so "Allow: /" wins for every path and it reports /chat as crawlable. An enforcement layer that says yes to a forbidden path is worse than no layer, because it gets believed. Replaced with an RFC 9309 matcher: longest matching pattern wins, Allow breaks exact ties, * and $ supported, named user-agent groups beat the wildcard group. Verified against the live policy - all eight paths now classify correctly, including /chat which the stdlib waved through. The check sits in front of the network in get_json, so a disallowed path raises Disallowed without a request being made. Cache hits skip it because no request happens either way. tests/test_robots.py pins the live policy verbatim, so a change on their side surfaces as a failure rather than as silent over-crawling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…base
robots.txt permits /ranking, /best-players and /player/* with no Crawl-delay,
and the ranking pages carry exactly the axes E1 needs - 3BV/s for speed, Eff
for efficiency, with leaderboard position as the skill label.
But the data is not reachable over plain HTTP. Every variant returns a
byte-identical 29,946-byte SPA shell reading "Loading data...": our UA, curl's
default, no UA, with and without a connect.sid cookie, with standard Accept
headers, and with ?page= / ?level= / path forms. No bot mitigation is present
(plain nginx) and the JS bundle references no data API. In a real browser the
DOM grows from ~30KB to ~49KB with the table, while the only same-origin XHR
is /authorize.
And /authorize creates a user account per call:
{"userId": 56457114, "isNewUser": true,
"user": {"createdAt": "2026-08-10T05:23:21.316Z", ...}}
Consecutive calls returned 56457114 then 56457116. Roughly four anonymous user
rows were created while diagnosing what was supposed to be a read-only
question.
So the binding constraint is not politeness or robots.txt, both of which are
satisfiable. It is that every available route either drives a real browser
thousands of times or writes to their production user table. Stopping pending
a reply from support@minesweeper.online or an official read-only endpoint.
coldopen/human/minesweeper_notes.md records the findings, including what to do
differently if it resumes: reuse one session, pseudonymise at ingest, and
treat display names as untrusted text.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Expert minesweeper as a solo timed env, for measuring per-click judgement alongside 3BV/s and click efficiency. Four design forks settled with the user: - guaranteed opening: mines are drawn from cells outside the closed 3x3 around the first click, so count(first_cell) == 0 by construction and no game is lost to first-move luck; - standard boards, not no-guess, matching the leaderboard we would test against - the solver then has to separate a FORCED guess from an avoidable one, which is the more informative feature; - reveal + flag + chord, because efficiency above 100% requires chording and efficiency is one of the two E1 axes; - the deduction oracle lives in the telemetry layer, not the env: constraint solving every step would make the batched env unusable, and the env does not need the answer for its own dynamics. One design point comes straight from E6. If every action cost the same, elapsed time would be exactly t*(COST+LATENCY), making 3BV/s equal to efficiency/(COST+LATENCY) - the two axes would be the same number in different units. That is the collinear-telemetry trap that broke sprint's multivariate transfer. Distinct per-kind costs (reveal 30, flag 30, chord 60, plus LATENCY) break the identity so speed and efficiency can vary independently. Mine keys are drawn at reset but applied at the first click, making the board a deterministic function of (seed, first_cell) and independent of how many flags preceded it. Keys rather than sampled positions because "the M smallest among eligible" cannot depend on draw order. 13 invariants and 1 RNG slot enumerated; schema.json mirrors the state table; every field is integral so no x-atol is declared anywhere. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Readable single-instance implementation: dataclass state, explicit ifs, every block tagged with the spec section it implements. No vectorization anywhere, so it stays an independent witness for the differential test against fast.py. Verified by hand against the spec's guarantees: - opening guarantee holds, count(first_cell) == 0 and the first click opened 49 cells; - exactly M=99 mines placed, none of them revealed while dead == 0; - win reachable (228 steps, won=1, reward +999770 = 1000000 - 230); - death reachable (reward -1000230 = -230 - 1000000); - determinism: same (seed, episode, actions) gives identical JSON, different seeds differ; - to_json/from_json round-trips exactly; - a no-op chord still costs 260ms and increments t, which efficiency depends on - an env that discarded useless clicks could not measure clicks-per-work; - chord is a no-op until flagged neighbours equal the count, then reveals in ascending index order; - flags block the flood: a flagged cell inside an opening stays hidden and holds back everything beyond it. MINE_KEYS uses rng.draw_bits directly (64 uniform bits) rather than masking to uint32 - every transformation is another place the two implementations can disagree. Spec amended to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fast.py written from spec.md, never from reference.py. The differential test caught a real independent-misreading on seed 1: a mine can have neighbour count 0 (count excludes the cell itself), and the batched flood seeded from any revealed zero-count cell - including the mine that just ended the episode - revealing the ring around it. The spec's Reveal sequence stops at step 3 for a mine; one ~mines term in the flood seed fixes it. Two other traps handled by design: mine keys masked to 32 bits (torch reinterprets draw_bits' uint64 as SIGNED, so unmasked ascending sorts disagree for half the key space), and the flood gated per-instance on "this step revealed a zero" (a global fixpoint would reveal cells when a flag is REMOVED). Battery: 10/10 PASS, validation_report.json committed - the gate before training, checked as an artifact this time. FullyConvNet: conv body -> 1x1 head, [B,3,H,W] output whose row-major flatten IS the kind-major action encoding. 49.5k parameters against BoardNet's 4.2M for the same job, and locality verified by perturbation. Minesweeper deduction is local translation-invariant pattern-matching, which is exactly what weight sharing buys; the Flatten+Linear head forced 1440 spatial actions through a 128-unit bottleneck and had to relearn each pattern per location. Also adds train_minesweeper.py: DQN with an exploration mask over spec-defined no-ops and potential-based shaping on revealed-safe progress. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The exploration wall in numbers: an expert win is ~381 correct reveals with zero fatal ones, and random play dies in a handful of clicks - sprint's wall again, met head-on this time instead of discovered after 3M wasted steps. ms_curriculum.py (after Salimans & Chen 2018): reset agents into boards with only k safe cells left; a Pacer moves the start line backwards whenever the windowed win rate clears a threshold, one-directional so the curriculum cannot thrash. The validated env is not touched - the subclass defers to the validated reset and then overwrites masked instances; its re-derived mine placement is pinned byte-for-byte to fast.py's by a parity test. Injected starts satisfy every invariant except I9, which defines t==0 as ungenerated - a curriculum start is deliberately mid-game, so training runs debug-off and validation stays on the base class. Start states come from a pluggable source, which is the answer to "use scraped replays as a curriculum later": - SyntheticStates (running now): the env's own boards with all but k random safe cells revealed. Cold-start legitimate, usable in the main ladder. - minesweeper_replays.py (contract now, data later): fixed JSON format for scraped games; clicks replay through the REFERENCE env so one validated flood/chord implementation defines what "after click i" looks like, and every visited state becomes a start state. Contamination rules fixed in advance: separate ladder directory, disjoint player split by hash, pseudonymised at the scraper, and the comparison framed as a point on the label-efficiency curve rather than a second cold-start method. Trainer switched to the FCN head and the curriculum env; local replay buffer because train_sprint's binds sprint's shapes at import. Fixture correction caught by our own spec: a flagged safe cell blocks the flood, so the toy replay does not win - both captured states are live. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-signal The DQN's measured failure mode by 116k steps: deaths falling (43 -> 18 on curriculum probes) with zero wins - it learns to avoid revealing rather than to locate safety, because with 99 hidden mines against a few hidden safe cells, "never reveal" beats an uninformed reveal, and a 1-in-1440 sparse Q-target gives almost no gradient toward finding the safe cell. mineprob.py replaces the sparse signal with a dense one. The env knows its mines, so every visited state yields 480 labelled cells free: FCN -> per-cell P(mine), BCE masked to unrevealed cells, policy = reveal argmin. States are collected under the net's own policy - the collector IS the policy, so there is no train/rollout gap to close (the tetris lesson), and the label is a pure function of the board, so there is no ill-posed teacher (the other tetris lesson). No human data anywhere; checkpoints form a cold-start judgement ladder whose dial is board-reading quality, with epsilon-lapses available as the degradation knob at the judgement level. Smoke test: 300 supervised updates already reveal 30-48 safe cells per game against the DQN's 0-24 after 262,000 env steps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v1's 2M-step 0.000-win result was the optimum of the reward it was given: an uninformed reveal is a mine with prior ~21% (expected value ~-210k) against flagging forever at -230 a step, and the +1 shaping bonus for a correct reveal was 0.4% of the step cost. Flag-spin was rational. Three training-only changes, env and spec untouched: - --shaping-weight 300: correct reveals become material, not rounding error; - --death-penalty 20k replaces the -1M term during training. -1M targets do not just discourage death - generalization smears massive negatives across ALL reveal Q-values, burying the safe-vs-unsafe spread the net must learn; - --aux-weight 1.0: a 4th head channel predicts mines, BCE against the env's own layout masked to unrevealed cells, on the live batch each loop. The A/B against mineprob showed the bottleneck is gradient density, not reward shape: same net and env, 480 labels/state reached 81.7 safe cells in 7k updates while 1 scalar/action reached 0 in 2M steps. mineprob final curve: 25 -> 81 safe cells over 16k updates, win rate still 0 - it reads boards locally but cannot yet finish one. Its checkpoints are a monotone-ish judgement ladder regardless; wins need either longer training, more depth, or the forced-guess endgame the solver will quantify. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eplays The sprint lesson applied before it bites this time: every human leaderboard record is a completed game, and no agent wins Expert yet - so the comparable ladders move to Beginner (9x9, 10) and Intermediate (16x16, 40), which have their own leaderboards on minesweeper.online. The env was always parameterized and the FCN is fully convolutional, so one architecture covers all three sizes unchanged. "The differential test passed" is only true of parameters it ran, and the battery runs Expert defaults - tests/test_ms_boards.py pins a miniature differential (300 steps x 2 seeds, bit-exact JSON) plus an invariant sweep at each extra size, and guards that an oracle can actually win a 9x9. The replay ingest contract now requires an `outcome` field with lost and abandoned games first-class. Player profiles record failures (win rate = wins/attempts); leaderboards cannot. The E4 metric is win rate + progress-at-death + telemetry rather than time-on-wins, so losses on both sides make the comparison denser instead of narrower. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y layer ms_solver.py decides the headline judgement feature by logic, not opinion: unary fixpoint, pairwise-subset rules, and exact enumeration over frontier components up to 16 cells, with `complete` reported honestly when a component was too big to enumerate. The oracle sees only what the player saw - revealed counts, never the layout, never the flags - so it runs identically on agent episodes and future scraped human games. Soundness is tested against live env games it cannot peek at: it never calls a mine safe or a safe cell a mine. ms_telemetry.py extracts both feature families per episode: the leaderboard-comparable numbers a human record carries (win, time, 3BV, 3BV/s, efficiency) and the judgement numbers only the oracle produces (proven-safe rate, avoidable-guess rate, forced-guess rate, blunder rate). The oracle is consulted BEFORE each click on the position as seen, so verdicts use exactly the information available at decision time. First judged measurement, top beginner checkpoint: 80.7% of reveals provably safe, 11.8% avoidable guesses, 4.7% blunders, 2.8% forced guesses - which is the win-rate ceiling explained, not just observed. 184 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Six checkpoints x three lapse rates, every reveal judged on the position as seen. Training moves proven-safe 0.10 -> 0.83 and blunders 0.08 -> 0.01; the lapse dial at a fixed checkpoint pushes blunders back to 0.19 while proven-safe falls more slowly. Two axes, distinguishable in telemetry - the anti-collinearity property sprint lacked, measured rather than hoped for. Agent side of E4 complete; human data remains the blocked input. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
192 oracle-judged rungs (6 checkpoints x 8 lapse rates x 4 latencies). Coverage now spans time 0.38-25s and 3BV/s 0.34-10.4; latency moves speed with judgement flat, training and lapses move judgement with speed flat, and win rate rides the judgement axis. The collinearity that sank sprint's multivariate transfer is absent by construction and confirmed by measurement. Lapse dial cliffs between 0.05 and 0.10 - sample that range densely in the human-comparison run, with more than 16 episodes per rung. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ding them coldopen/ladders is gitignored because .pt files are large and regenerable. train_log.json is neither: it is the measured curve, and several of this project's findings ARE curves - beginner mineprob 3% -> 67%, the DQN's flat zero across 2M steps, mineprob expert 25 -> 81 safe cells, the sprint distillation plateau. Those were committed only as prose in EXPERIMENTS.md with no data behind them. All 18 curves are now mirrored into analysis/training_curves/ with an INDEX, and .gitignore says to repeat that after any run whose curve gets cited. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Debug snapshots from fixing the tetris I11 lock-residual bug and confirming the curriculum's deliberate I9 exception. Both are documented in EXPERIMENTS.md and pinned by tests; the raw dumps served their purpose. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mwburke
added a commit
that referenced
this pull request
Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
68 commits building the agent track end to end across two new environments, plus the experiments that tested whether any of it predicts human skill.
What was built
Two validated environments (simulacrum workflow: spec → reference → batched → differential battery)
tetris_sprint— input-level 40 LINES on a virtual clock, 12/12 battery PASSminesweeper— expert/intermediate/beginner, 10/10 battery PASS, 13 invariantsAgent generators
Analysis: E6 transfer test, label-efficiency curves, accuracy cliff, 192-rung three-dial grid
Headline findings
E6 (sprint) — the simulator lost to one number, then won it back. Ranking players by
ppsalone scores ρ=0.932 against the agent-fitted model's 0.660. But that baseline is not cold-start legitimate: knowing which axis to rank by requires labelled human data. Priced honestly, a model fitted only on agents is never beaten by one fitted on humans at any sample size tested — worth more than 300 labelled games.Signal density, not reward shape, is the RL bottleneck. Same net, same env: 1 sparse scalar per action gave 0 safe cells after 2M steps; 480 dense labels per state gave 81 after 16k updates.
The accuracy cliff. Nothing finishes a sprint below 95% per-placement imitation accuracy, so imitation accuracy cannot serve as a skill dial — it is a step function, not a gradient.
Minesweeper's ladder decorrelates where sprint's did not. Across 192 oracle-judged rungs, speed × judgement r = −0.03 (sprint: +0.85). Win rate rides the judgement axis (+0.64), not speed (+0.03).
Corrections made mid-flight
Three claims I published and then had to retract: column 9 was never unreachable, the env validation gate was never actually green (re-run: PASS), and every distilled number before the sampling fix was measuring an argmax deadlock. All documented in
EXPERIMENTS.mdrather than quietly fixed.Not included, deliberately
Checkpoint weights (regenerable) and
data/human/(belongs to its publishers; personal data even pseudonymised).🤖 Generated with Claude Code